package com.zjh.desensitive;

import com.alibaba.fastjson.JSON;
import com.zjh.desensitive.entity.StaticDesensitive;
import com.zjh.desensitive.entity.StaticDesensitiveDataSource;
import com.zjh.desensitive.entity.StaticDesensitiveWriter;
import com.zjh.desensitive.entity.TargetColumn;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DesensitiveMain {
    public static void main(String[] args) throws IOException {
        if(args.length != 0 || StringUtils.isNotEmpty(args[0])){
            String json = readFile(args[0]);
            StaticDesensitive staticDesensitive = JSON.parseObject(json, StaticDesensitive.class);
            JdbcTemplate jdbcTemplateReader = getJdbcTemplate(staticDesensitive.getReader().getDataSource());
            JdbcTemplate jdbcTemplateWriter = getJdbcTemplate(staticDesensitive.getWriter().getDataSource());

            String insertSql = createInsertSql(staticDesensitive.getWriter());

            List<TargetColumn> target_columns = staticDesensitive.getWriter().getTarget_columns();
            final Map<String, TargetColumn> targetColumnsMap = transformTargetColumnsMap(target_columns);

            final List<Map<String, Object>> maps = jdbcTemplateReader.queryForList(staticDesensitive.getReader().getSql_select());

            jdbcTemplateWriter.batchUpdate(insertSql, new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    Map<String, Object> stringObjectMap = maps.get(i);
                    Object[] keys = stringObjectMap.keySet().toArray();

                    for(int j = 1; j <= keys.length; j ++){
                        String key = (String) keys[j - 1];
                        TargetColumn targetColumn = targetColumnsMap.get(key);

                        if(targetColumn.getType_class().equals(java.lang.String.class.toString())){
                            String value = (String) stringObjectMap.get(keys[j - 1]);
                            value = (String) desensitiveByTargetColumn(targetColumn, value);
                            ps.setString(j, value);
                        }else if(targetColumn.getType_class().equals(java.lang.Integer.class.toString())){
                            Integer value = (Integer) stringObjectMap.get(keys[j - 1]);
                            value = (Integer) desensitiveByTargetColumn(targetColumn, value);
                            ps.setInt(j, value);
                        }else{
                            throw new SQLException(targetColumn.getType_class() + ", 该字段对象类型不支持");
                        }
                    }
                }
                public int getBatchSize() {
                    return maps.size();
                }

            });
        }else{
            throw new IOException("static-desensitive-job.json文件未找到");
        }
    }

    private static Object desensitiveByTargetColumn(TargetColumn targetColumn, Object value) {
        try{
            String desensitive_class = targetColumn.getDesensitive_class();
            String method = targetColumn.getMethod();
            String class_path = targetColumn.getClass_path();
            String regular = targetColumn.getRegular();

            if(StringUtils.isEmpty(desensitive_class)
                    || StringUtils.isEmpty(method)
                    || StringUtils.isEmpty(class_path)
                    || StringUtils.isEmpty(regular)){
                return value;
            }

            Pattern p = Pattern.compile(regular);
            Matcher matcher = p.matcher(String.valueOf(value));
            if(matcher.find()){
                URL url = new File(class_path).toURI().toURL();
                URLClassLoader classLoader = new URLClassLoader(new URL[] { url },
                        DesensitiveMain.class.getClassLoader());

                Class clazz = classLoader.loadClass(desensitive_class);
                Object obj = clazz.getDeclaredConstructor().newInstance();

                Class[] paramsClassArgs = new Class[0];
                if(value instanceof java.lang.String){
                    paramsClassArgs = new Class[]{String.class};
                }else if(value instanceof java.lang.Integer){
                    paramsClassArgs = new Class[]{Integer.class};
                }

                Method handleFileMethod = clazz.getMethod(method, paramsClassArgs);

                Object[] arguments =new Object[]{value};
                return handleFileMethod.invoke(obj, arguments);
            }else{
                return value;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Map<String, TargetColumn> transformTargetColumnsMap(List<TargetColumn> target_columns) {
        Map<String, TargetColumn> map = new HashMap<String, TargetColumn>();
        for(TargetColumn targetColumn : target_columns){
            String name = targetColumn.getName();
            map.put(name, targetColumn);
        }
        return map;
    }

    private static String createInsertSql(StaticDesensitiveWriter writer) {
        String target_table = writer.getTarget_table();
        List<TargetColumn> target_columns = writer.getTarget_columns();
        String columns = "";
        String values = "";
        for(TargetColumn column : target_columns){
            String name = column.getName();
            columns = columns + "`" + name + "`,";
            values = values + "?,";
        }
        columns = StringUtils.substringBeforeLast(columns, ",");
        values = StringUtils.substringBeforeLast(values, ",");

        String sql = String.format("insert into %s(%s) value (%s)", target_table, columns, values);
        return sql;
    }


    public static String readFile(String jsonFile) {
        StringBuilder stringBuilder = new StringBuilder();
        try{
            FileInputStream fileInputStream = new FileInputStream(jsonFile);
            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String tempString;
            while ((tempString = bufferedReader.readLine()) != null){
                stringBuilder.append(tempString);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }

    public static JdbcTemplate getJdbcTemplate(StaticDesensitiveDataSource datasource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(datasource.getDriverClass());
        dataSource.setUrl(datasource.getUrl());
        dataSource.setUsername(datasource.getUser());
        dataSource.setPassword(datasource.getPassword());
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}
